﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using DataCenter.Share;

namespace DataCenter.Interface.Model
{
    public class ObjectEntity
    {
        private int objectId;
        private int objectType;
        private string objectName;
        private string objectKey;
        private bool isVirtual;
        private int bindingSourceId = -1;
        private int bindingAdapterId = -1;
        private int bindingProcessorId = -1;
        private int bindingCalculatorId = -1;
        private float defaultValue = 0.0f;
        private float unit = 1.0f;
        private bool isComputeObject;
        private SortedList<DateTime, BinaryRecord> recordList;

        public int ObjectId
        {
            get { return objectId; }
            set { objectId = value; }
        }

        public int ObjectType
        {
            get { return objectType; }
            set { objectType = value; }
        }

        public string ObjectName
        {
            get { return objectName; }
            set { objectName = value; }
        }

        public string ObjectKey
        {
            get { return objectKey; }
            set { objectKey = value; }
        }

        public bool IsVirtual
        {
            get { return isVirtual; }
            set { isVirtual = value; }
        }

        public int BindingSourceId
        {
            get { return bindingSourceId; }
            set { bindingSourceId = value; }
        }

        public int BindingAdapterId
        {
            get { return bindingAdapterId; }
            set { bindingAdapterId = value; }
        }

        public int BindingProcessorId
        {
            get { return bindingProcessorId; }
            set { bindingProcessorId = value; }
        }

        public int BindingCalculatorId
        {
            get { return bindingCalculatorId; }
            set { bindingCalculatorId = value; }
        }

        public float DefaultValue
        {
            get { return defaultValue; }
            set { defaultValue = value; }
        }

        public float Unit
        {
            get { return unit; }
            set { unit = value; }
        }

        public bool CanImport
        {
            get { return (bindingSourceId > 0 && bindingAdapterId > 0); }
        }

        public bool CanProcess
        {
            get { return (bindingProcessorId > 0); }
        }

        public bool CanCalculate
        {
            get { return (bindingCalculatorId > 0); }
        }

        public bool IsComputeObject
        {
            get { return isComputeObject; }
            set { isComputeObject = value; }
        }

        public SortedList<DateTime, BinaryRecord> RecordList
        {
            get { return recordList; }
            set { recordList = value; }
        }

        public static List<ObjectEntity> SearchAsList(SqlCommand command, string objectIds)
        {
            string commandString = "SELECT * FROM [Object] WHERE 1 = 1";
            if (!string.IsNullOrEmpty(objectIds))
                commandString += " AND ObjectId IN (" + objectIds + ")";
            commandString += " ORDER BY ObjectId, ObjectType";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;

            List<ObjectEntity> objectList = new List<ObjectEntity>();
            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    ObjectEntity objectEntity = new ObjectEntity();
                    objectEntity.Build(reader);
                    objectList.Add(objectEntity);
                }
            }
            return objectList;
        }
        public static Dictionary<int, ObjectEntity> Search(SqlCommand command, string objectIds)
        {
            string commandString = "SELECT * FROM [Object] WHERE 1 = 1";
            if (!string.IsNullOrEmpty(objectIds))
                commandString += " AND ObjectId IN (" + objectIds + ")";
            commandString += " ORDER BY ObjectId, ObjectType";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;

            Dictionary<int, ObjectEntity> dictObject = 
                new Dictionary<int, ObjectEntity>();
            using (SqlDataReader reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    ObjectEntity objectEntity = new ObjectEntity();
                    objectEntity.Build(reader);
                    if (!dictObject.ContainsKey(objectEntity.objectId))
                        dictObject.Add(objectEntity.objectId, objectEntity);
                }
            }
            return dictObject;
        }
        public static int SearchMaxId(SqlCommand command)
        {
            string commandString = "SELECT MAX(ObjectId) FROM [Object]";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            object oValue = command.ExecuteScalar();
            if (oValue != null && oValue != DBNull.Value)
                return Convert.ToInt32(oValue);
            return 0;
        }
        public static bool CheckRepeat(SqlCommand command, int objectId)
        {
            string commandString = "SELECT ObjectId FROM [Object] WHERE ObjectId = " + objectId;
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            object oValue = command.ExecuteScalar();
            return (oValue != null && oValue != DBNull.Value);
        }

        public bool Create(SqlCommand command)
        {
            string fieldString = "ObjectId, ObjectType, ObjectName, ObjectKey, VirtualObject, BindingSourceId, BindingAdapterId, BindingProcessorId, BindingCalculatorId, DefaultValue, Unit";
            string valueString = "@ObjectId, @ObjectType, @ObjectName, @ObjectKey, @VirtualObject, @BindingSourceId, @BindingAdapterId, @BindingProcessorId, @BindingCalculatorId, @DefaultValue, @Unit";          
            string commandString = "INSERT INTO [Object](" + fieldString + ") VALUES(" + valueString + ")";            

            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ObjectId", objectId);
            command.Parameters.AddWithValue("@ObjectType", objectType);
            command.Parameters.AddWithValue("@ObjectName", objectName);
            command.Parameters.AddWithValue("@ObjectKey", objectKey);
            command.Parameters.AddWithValue("@VirtualObject", isVirtual ? 1 : 0);
            command.Parameters.AddWithValue("@BindingSourceId", bindingSourceId);
            command.Parameters.AddWithValue("@BindingAdapterId", bindingAdapterId);
            command.Parameters.AddWithValue("@BindingProcessorId", bindingProcessorId);
            command.Parameters.AddWithValue("@BindingCalculatorId", bindingCalculatorId);
            command.Parameters.AddWithValue("@DefaultValue", defaultValue);   
            command.Parameters.AddWithValue("@Unit", unit);
            command.ExecuteNonQuery();
            command.Parameters.Clear();

            return true;
        }
        public bool Modify(SqlCommand command)
        {
            string commandString = @"UPDATE [Object] SET ObjectType=@ObjectType,
                                                         ObjectName=@ObjectName,
                                                         ObjectKey=@ObjectKey,
                                                         VirtualObject=@VirtualObject,
                                                         BindingSourceId=@BindingSourceId,
                                                         BindingAdapterId=@BindingAdapterId,
                                                         BindingProcessorId=@BindingProcessorId,
                                                         BindingCalculatorId=@BindingCalculatorId,
                                                         DefaultValue=@DefaultValue,       
                                                         Unit=@Unit                                                         
                                    WHERE ObjectId=@ObjectId";

            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ObjectId", objectId);
            command.Parameters.AddWithValue("@ObjectType", objectType);
            command.Parameters.AddWithValue("@ObjectName", objectName);
            command.Parameters.AddWithValue("@ObjectKey", objectKey);
            command.Parameters.AddWithValue("@VirtualObject", isVirtual ? 1 : 0);
            command.Parameters.AddWithValue("@BindingSourceId", bindingSourceId);
            command.Parameters.AddWithValue("@BindingAdapterId", bindingAdapterId);
            command.Parameters.AddWithValue("@BindingProcessorId", bindingProcessorId);
            command.Parameters.AddWithValue("@BindingCalculatorId", bindingCalculatorId);
            command.Parameters.AddWithValue("@DefaultValue", defaultValue);
            command.Parameters.AddWithValue("@Unit", unit);
            command.ExecuteNonQuery();
            command.Parameters.Clear();

            return true;
        }
        public bool Drop(SqlCommand command)
        {
            string commandString = "DELETE FROM [Object] WHERE ObjectId=@ObjectId";
            command.CommandText = commandString;
            command.CommandType = CommandType.Text;
            command.Parameters.AddWithValue("@ObjectId", objectId);
            command.ExecuteNonQuery();
            command.Parameters.Clear();
            return true;
        }
        public void Build(SqlDataReader reader)
        {
            object oValue = reader["ObjectId"];
            if (oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString()))
                throw new NullReferenceException("字段\"ObjectId\"为空。");
            if (!int.TryParse(oValue.ToString(), out objectId))
                throw new InvalidCastException("字段\"ObjectId\"的值\"" + oValue + "\"不正确。");
            objectName = objectId.ToString();

            oValue = reader["ObjectName"];
            if (oValue != DBNull.Value && !string.IsNullOrEmpty(oValue.ToString()))
                objectName = oValue.ToString();

            oValue = reader["ObjectType"];
            if (oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString()))
                throw new NullReferenceException("对象\"" + objectId + "\"的字段\"ObjectType\"为空。");
            if (!int.TryParse(oValue.ToString(), out objectType))
                throw new InvalidCastException("对象\"" + objectId + "\"的字段\"ObjectType\"的值\"" + oValue + "\"不正确。");

            oValue = reader["ObjectKey"];
            if (oValue == DBNull.Value || string.IsNullOrEmpty(oValue.ToString()))
                throw new NullReferenceException("对象\"" + objectId + "\"的字段\"ObjectKey\"为空。");
            objectKey = oValue.ToString();

            oValue = reader["VirtualObject"];
            if (oValue != DBNull.Value && !string.IsNullOrEmpty(oValue.ToString()))
            {
                int iValue;
                if (int.TryParse(oValue.ToString(), out iValue))
                    isVirtual = Convert.ToBoolean(iValue);
            }

            oValue = reader["BindingSourceId"];
            if (oValue != DBNull.Value && !string.IsNullOrEmpty(oValue.ToString()))
            {
                if (!int.TryParse(oValue.ToString(), out bindingSourceId))
                    bindingSourceId = -1;
            }

            oValue = reader["BindingAdapterId"];
            if (oValue != DBNull.Value && !string.IsNullOrEmpty(oValue.ToString()))
            {
                if (!int.TryParse(oValue.ToString(), out bindingAdapterId))
                    bindingAdapterId = -1;
            }

            oValue = reader["BindingProcessorId"];
            if (oValue != DBNull.Value && !string.IsNullOrEmpty(oValue.ToString()))
            {
                if (!int.TryParse(oValue.ToString(), out bindingProcessorId))
                    bindingProcessorId = -1;
            }

            if (isVirtual)
            {
                oValue = reader["BindingCalculatorId"];
                if (oValue != DBNull.Value && !string.IsNullOrEmpty(oValue.ToString()))
                {
                    if (!int.TryParse(oValue.ToString(), out bindingCalculatorId))
                        bindingCalculatorId = -1;
                }
            }

            oValue = reader["DefaultValue"];
            if (oValue != DBNull.Value && !string.IsNullOrEmpty(oValue.ToString()))
            {
                float fValue;
                if (float.TryParse(oValue.ToString(), out fValue))
                    defaultValue = fValue;
            }

            oValue = reader["Unit"];
            if (oValue != DBNull.Value && !string.IsNullOrEmpty(oValue.ToString()))
            {
                if (!float.TryParse(oValue.ToString(), out unit))
                {
                    unit = 1.0f;
                }
            }
        }

        public int[] GetComputeObjectIds()
        {
            List<int> idList = new List<int>();
            MatchCollection matches = Regex.Matches(objectKey, @"(\[\s*)(\d+)(\s*\])");
            foreach (Match match in matches)
            {
                if (match.Success)
                {
                    string idStr = match.Value.Replace("[", "").Replace("]", "");
                    idList.Add(Convert.ToInt32(idStr));
                }
            }
            return idList.ToArray();
        }

        public bool ContainRecord(DateTime valueTime)
        {
            if (recordList != null)
            {
                return recordList.ContainsKey(valueTime.Date);
            }
            return false;
        }
        public void AddRecord(BinaryRecord record)
        {
            if (recordList == null)
                recordList = new SortedList<DateTime, BinaryRecord>();
            if (!recordList.ContainsKey(record.ValueTime))
                recordList.Add(record.ValueTime, record);
        }
        public void RemoveRecord(DateTime valueTime)
        {
            if (recordList != null)
            {
                if (recordList.ContainsKey(valueTime.Date))
                    recordList.Remove(valueTime.Date);
            }
        }
        public BinaryRecord GetRecord(DateTime valueTime)
        {
            if (recordList != null)
            {
                if (recordList.ContainsKey(valueTime.Date))
                    return recordList[valueTime.Date];
            }
            return null;
        }
        public BinaryRecord SearchRecord(SqlCommand command, DateTime valueTime, bool autoSave)
        {
            TableSetting setting = TableSetting.GetInstance();

            if (setting.IsTableExist(valueTime))
            {
                string commandTemplet = @"SELECT ObjectId, ValueTime, ValueType, ImportTime, InterpolateTime, BinaryValue 
                                          FROM {0} WHERE ValueTime = '" + valueTime.Date + @"' AND ObjectId = " + objectId;
                string tableName = setting.GetTableName(valueTime);
                string commandString = string.Format(commandTemplet, tableName);

                command.CommandText = commandString;
                command.CommandType = CommandType.Text;
                using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
                {
                    while (reader.Read())
                    {
                        BinaryRecord record = new BinaryRecord();
                        record.Build(reader);
                        record.Build(reader, false);
                        if (autoSave) 
                            AddRecord(record);
                        return record;
                    }
                }
            }

            return null;
        }
        public void SearchRecord(SqlCommand command, DateTime time0, DateTime time1, bool isCache)
        {
            TableSetting setting = TableSetting.GetInstance();

            int sIndex = 0, eIndex = 0;
            if (setting.AdjustTimeRegion(time0, time1, ref sIndex, ref eIndex))
            {
                string commandTemplet = @"SELECT ObjectId, ValueTime, ValueType, ImportTime, InterpolateTime, BinaryValue 
                                        FROM {0} WHERE ValueTime >= '" + time0.Date + @"' AND ValueTime <= '" + time1.Date + @"' AND ObjectId IN (" + objectId + @")";

                StringBuilder cmdStrBuilder = new StringBuilder();
                for (int index = sIndex; index <= eIndex; index++)
                {
                    string tableName = setting.GetTableName(index);

                    cmdStrBuilder.Append(" UNION ");
                    cmdStrBuilder.AppendFormat(commandTemplet, tableName);

                    if (cmdStrBuilder.Length > 7500 || index == eIndex)
                    {
                        string commandString = cmdStrBuilder.ToString();
                        commandString = commandString.Substring(7);

                        command.CommandText = commandString;
                        command.CommandType = CommandType.Text;
                        using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
                        {
                            while (reader.Read())
                            {
                                BinaryRecord record = new BinaryRecord();
                                record.Build(reader);
                                record.Build(reader, isCache);
                                AddRecord(record);
                            }
                        }
                    }
                }
            }
        }
        public void ClearRecord()
        {
            if (recordList != null)
            {
                recordList.Clear();
                recordList = null;
            }
        }

        public override string ToString()
        {
            return objectName + "(" + objectId + ")";
        }
    }
}
